local wxGUI = require "stab.scripts.init.wxGUI"
local wxOperatorStack = require "stab.scripts.init.wxOperatorStack"
local wxNodePanel = class("wxNodePanel")

local DEFAULT_TEXT_WIDTH = 150
local DEFAULT_FUNCTION_WIDTH = 110
local DEFAULT_FUNCTION_HEIGHT = 30
local DESIGN_RESOLUTION_WIDTH = 1136
local DESIGN_RESOLUTION_HEIGHT = 640
local DEFAULT_MOVED_OPACITY = 150
local UI_CAMERA_FLAG = cc.CameraFlag.DEFAULT
local WORLD_CAMERA_FLAG = cc.CameraFlag.USER1
local TOUCH_PANEl_ORDER = 999999
local CIRCLE_R = 100

-- 示例操作类定义
local Operation = {}
Operation.new = function(this, node, data, oldValue, newValue)
    local op = {}
    op.node = node
    --当前执行
    op.redo = function()
        if data.set then
            if node and not tolua.isnull(node) then
                if newValue or newValue == false then
                    node[data.set](node, newValue)
                end
            end
        else
            if data.callBack then
                data.callBack(this, newValue)
            end
        end
    end

    --回撤方法
    op.undo = function()
        if data.set then
            if node and not tolua.isnull(node) then
                if oldValue or oldValue == false then
                    node[data.set](node, oldValue)
                end
            end
        else
            if data.callBack then
                data.callBack(this, oldValue)
            end
        end
    end
    return op
end

local TITLE = {
    {
        type = wxGUI.ComponentType.CHECKBOX,
        text = UTF8TowxString("打开选择功能"),
        getValue = function(self)
            if self.touchPanel and not tolua.isnull(self.touchPanel) then
                self.touchPanel:setSwallowTouches(self:getSwallowTouchStatus())
            end
            return self.touthEnable
        end,
        callBack = function(self, value)
            self.touthEnable = value
            if self.touthEnable then
                self.moveEnable = false
                self:cleanMoveNode()
            end
            self.preTouchNode = nil
            self.touchNode = nil
        end,
        transDataFromWx = function(b)
            return b and true or false
        end,
        transDataFromCocos = function(b)
            return b and true or false
        end,
        force = true,
        space = {x = 10, y = 0},
        ignoreOperatorStack = true
    },
    {
        type = wxGUI.ComponentType.CHECKBOX,
        text = UTF8TowxString("场景摄像机"),
        getValue = function(self)
            local count = 0
            local isOpen = false
            for i, v in pairs(self.cameras) do
                count = count + 1
                if count > 1 then
                    isOpen = true
                    break
                end
            end
            self.OpenWorldCamera = isOpen and self.OpenWorldCamera
            return self.OpenWorldCamera
        end,
        callBack = function(self, value)
            self.OpenWorldCamera = value
            self:calculateCamera()
        end,
        transDataFromWx = function(b)
            return b and true or false
        end,
        transDataFromCocos = function(b)
            return b and true or false
        end,
        force = true,
        space = {x = 100, y = 0},
        ignoreOperatorStack = true
    },
    {
        type = wxGUI.ComponentType.EDITBOX,
        title = UTF8TowxString("更新频率"),
        getValue = function(self)
            return self.infoData.interval
        end,
        callBack = function(self, value)
            self.infoData.interval = value
        end,
        transDataFromWx = function(value)
            return wxTonumber(value)
        end,
        transDataFromCocos = function(interval)
            local roundedNum = string.format("%.3f", interval)
            return tostring(tonumber(roundedNum))
        end,
        width = 50,
        height = 25,
        nospace = true,
        force = true,
        space = {x = 85, y = 0}
    },
    {
        type = wxGUI.ComponentType.EDITBOX,
        text = UTF8TowxString("游戏速度"),
        title = UTF8TowxString("游戏速度"),
        getValue = function(self)
            local director = cc.Director:getInstance()
            return director:getScheduler():getTimeScale()
        end,
        callBack = function(self, value)
            local director = cc.Director:getInstance()
            if not value then
                return
            end
            value = (value < 0) and 0 or value
            --防止小于0
            director:getScheduler():setTimeScale(value)
        end,
        transDataFromWx = function(value)
            return wxTonumber(value)
        end,
        transDataFromCocos = function(scale)
            local roundedNum = string.format("%.3f", scale)
            return tostring(tonumber(roundedNum))
        end,
        width = 50,
        height = 25,
        nospace = true,
        force = true,
        space = {x = DEFAULT_FUNCTION_WIDTH - 50, y = 0}
    },
    {
        type = wxGUI.ComponentType.BUTTON,
        text = UTF8TowxString("撤回"),
        width = 50,
        callBack = function(self)
            self.wxOperatorStack:undo()
        end,
        force = true,
        space = {x = DEFAULT_FUNCTION_WIDTH - 50, y = 0},
        ignoreOperatorStack = true
    },
    {
        type = wxGUI.ComponentType.BUTTON,
        text = UTF8TowxString("还原"),
        width = 50,
        callBack = function(self)
            self.wxOperatorStack:redo()
        end,
        force = true,
        space = {x = DEFAULT_FUNCTION_WIDTH - 50, y = 0},
        ignoreOperatorStack = true
    }
}

local layoutTable = {
    children = {
        {
            type = wxGUI.ComponentType.CHECKBOX,
            get = "isVisible",
            set = "setVisible",
            text = "Node",
            transDataFromWx = function(b)
                return b and true or false
            end,
            transDataFromCocos = function(b)
                return b and true or false
            end
        },
        {
            type = wxGUI.ComponentType.EDITBOX,
            get = "getName",
            set = "setName",
            transDataFromWx = function(str)
                return str
            end,
            transDataFromCocos = function(str)
                return str
            end,
            space = {x = 70, y = 0}
        },
        {
            type = wxGUI.ComponentType.BUTTON,
            callBack = function(self)
                self:drawRect(self.infoData.chooseNode, 3)
            end,
            text = "draw",
            force = true,
            space = {x = DEFAULT_FUNCTION_WIDTH, y = 0},
            ignoreOperatorStack = true
        },
        {
            type = wxGUI.ComponentType.BUTTON,
            callBack = function(self)
                local file = self:calcBindFile()
                self:openFile(file)
            end,
            -- visible = function(self)
            --     if self.nodeBindfile and self.nodeBindfile~="" then
            --         return true
            --     end
            --     return false
            -- end,
            text = UTF8TowxString("打开绑定Lua"),
            force = true,
            space = {x = 120, y = 0},
            ignoreOperatorStack = true
        },
        {
            type = wxGUI.ComponentType.BUTTON,
            callBack = function(self)
                local file = self:calcPathFile()
                self:reloadFile(file)
            end,
            text = UTF8TowxString("刷新文件"),
            force = true,
            space = {x = 120, y = 0},
            ignoreOperatorStack = true
        },
        {
            type = wxGUI.ComponentType.CHECKBOX,
            text = UTF8TowxString("锁定节点"),
            getValue = function(self)
                if self.touchPanel and not tolua.isnull(self.touchPanel) then
                    self.touchPanel:setSwallowTouches(self:getSwallowTouchStatus())
                end
                if self.moveNode and not tolua.isnull(self.moveNode) then
                    return self.moveEnable
                else
                    self:cleanMoveNode()
                    self.moveEnable = false
                    return self.moveEnable
                end
            end,
            callBack = function(self, value)
                self.moveEnable = value
                if self.moveEnable then
                    self.touthEnable = false
                    self.preTouchNode = nil
                    self.touchNode = nil
                end
                if self.moveEnable then
                    self.moveNode = self.chooseNode
                    self.moveNodeRect = self:drawRect(self.moveNode)
                else
                    self:cleanMoveNode()
                end
            end,
            transDataFromWx = function(b)
                return b and true or false
            end,
            transDataFromCocos = function(b)
                return b and true or false
            end,
            force = true,
            space = {x = 0, y = 0},
            ignoreOperatorStack = true,
            changeLine = true
        },
        {
            type = wxGUI.ComponentType.EDITBOX,
            get = "getString",
            set = "setString",
            title = "string",
            transDataFromWx = function(str)
                return str
            end,
            transDataFromCocos = function(str)
                return str
            end,
            changeLine = true
        },
        {
            type = wxGUI.ComponentType.EDITBOX,
            get = "getTitleText",
            set = "setTitleText",
            title = "string",
            transDataFromWx = function(str)
                return str
            end,
            transDataFromCocos = function(str)
                return str
            end,
            changeLine = true
        },
        {
            type = wxGUI.ComponentType.COLORPICK,
            get = "getTitleColor",
            set = "setTitleColor",
            width = 50,
            height = 25,
            transDataFromWx = function(newColor)
                local r, g, b, a = newColor:Red(), newColor:Green(), newColor:Blue(), newColor:Alpha()
                return cc.c3b(r, g, b)
            end,
            transDataFromCocos = function(color)
                return wxGUI:getWXColor(color.r, color.g, color.b, 255)
            end,
            space = {x = DEFAULT_FUNCTION_WIDTH, y = 0}
        },
        {
            type = wxGUI.ComponentType.EDITBOX,
            get = "getPositionX",
            set = "setPositionX",
            title = "X",
            transDataFromWx = function(x)
                return wxTonumber(x)
            end,
            transDataFromCocos = function(x)
                return tostring(math.ceil(x))
            end,
            changeLine = true
        },
        {
            type = wxGUI.ComponentType.EDITBOX,
            get = "getPositionY",
            set = "setPositionY",
            title = "Y",
            transDataFromWx = function(y)
                return wxTonumber(y)
            end,
            transDataFromCocos = function(y)
                return tostring(math.ceil(y))
            end,
            space = {x = DEFAULT_FUNCTION_WIDTH, y = 0}
            --changeLine = true
        },
        {
            type = wxGUI.ComponentType.EDITBOX,
            get = "getContentSize",
            set = "setContentSize",
            title = "width",
            transDataFromWx = function(width, node)
                width = wxTonumber(width)
                if width then
                    local size = node:getContentSize()
                    size.width = width
                    return size
                end
                return nil
            end,
            transDataFromCocos = function(size)
                local roundedNum = string.format("%.3f", size.width)
                return tostring(tonumber(roundedNum))
            end,
            changeLine = true
        },
        {
            type = wxGUI.ComponentType.EDITBOX,
            get = "getContentSize",
            set = "setContentSize",
            title = "height",
            transDataFromWx = function(height, node)
                height = wxTonumber(height)
                if height then
                    local size = node:getContentSize()
                    size.height = height
                    return size
                end
                return nil
            end,
            transDataFromCocos = function(size)
                local roundedNum = string.format("%.3f", size.height)
                return tostring(tonumber(roundedNum))
            end,
            space = {x = DEFAULT_FUNCTION_WIDTH, y = 0}
        },
        {
            type = wxGUI.ComponentType.EDITBOX,
            get = "getAnchorPoint",
            set = "setAnchorPoint",
            title = "AnchorPoint_X",
            transDataFromWx = function(x, node)
                x = wxTonumber(x)
                if x then
                    local pt = node:getAnchorPoint()
                    pt.x = x
                    return pt
                end
                return nil
            end,
            transDataFromCocos = function(anchorPoint)
                local roundedNum = string.format("%.3f", anchorPoint.x)
                return tostring(tonumber(roundedNum))
            end,
            changeLine = true
        },
        {
            type = wxGUI.ComponentType.EDITBOX,
            get = "getAnchorPoint",
            set = "setAnchorPoint",
            title = "AnchorPoint_Y",
            transDataFromWx = function(y, node)
                y = wxTonumber(y)
                if y then
                    local pt = node:getAnchorPoint()
                    pt.y = y
                    return pt
                end
                return nil
            end,
            transDataFromCocos = function(anchorPoint)
                local roundedNum = string.format("%.3f", anchorPoint.y)
                return tostring(tonumber(roundedNum))
            end,
            space = {x = DEFAULT_FUNCTION_WIDTH, y = 0}
        },
        {
            type = wxGUI.ComponentType.EDITBOX,
            get = "getScaleX",
            set = "setScaleX",
            title = "Scale_X",
            transDataFromWx = function(x)
                return wxTonumber(x)
            end,
            transDataFromCocos = function(x)
                local roundedNum = string.format("%.3f", x)
                return tostring(tonumber(roundedNum))
            end,
            changeLine = true
        },
        {
            type = wxGUI.ComponentType.EDITBOX,
            get = "getScaleY",
            set = "setScaleY",
            title = "Scale_Y",
            transDataFromWx = function(y)
                return wxTonumber(y)
            end,
            transDataFromCocos = function(y)
                local roundedNum = string.format("%.3f", y)
                return tostring(tonumber(roundedNum))
            end,
            space = {x = DEFAULT_FUNCTION_WIDTH, y = 0}
        },
        {
            type = wxGUI.ComponentType.EDITBOX,
            get = "getRotation",
            set = "setRotation",
            title = "Rotation",
            transDataFromWx = function(y)
                return wxTonumber(y)
            end,
            transDataFromCocos = function(y)
                local roundedNum = string.format("%.3f", y)
                return tostring(tonumber(roundedNum))
            end,
            changeLine = true
        },
        {
            type = wxGUI.ComponentType.COLORPICK,
            get = "getColor",
            set = "setColor",
            title = "Color",
            width = 50,
            height = 25,
            transDataFromWx = function(newColor)
                local r, g, b, a = newColor:Red(), newColor:Green(), newColor:Blue(), newColor:Alpha()
                return cc.c3b(r, g, b)
            end,
            transDataFromCocos = function(color)
                return wxGUI:getWXColor(color.r, color.g, color.b, 255)
            end,
            changeLine = true
        },
        {
            type = wxGUI.ComponentType.EDITBOX,
            get = "getOpacity",
            set = "setOpacity",
            title = UTF8TowxString("透明度(0~255)"),
            width = 50,
            height = 25,
            transDataFromWx = function(opa)
                opa = wxTonumber(opa)
                if opa then
                    if opa < 0 then
                        opa = 0
                    end
                    if opa > 255 then
                        opa = 255
                    end
                end
                return opa
            end,
            transDataFromCocos = function(opa)
                local roundedNum = string.format("%.3f", opa)
                return tostring(tonumber(roundedNum))
            end,
            space = {x = DEFAULT_FUNCTION_WIDTH, y = 0}
        },
        {
            type = wxGUI.ComponentType.TEXTBOX,
            title = UTF8TowxString("输入脚本:"),
            getValue = function(self)
                return self.script
            end,
            callBack = function(self, value)
                self.script = value
            end,
            force = true,
            width = 350,
            height = 80,
            changeLine = true,
            ignoreOperatorStack = true
        },
        {
            type = wxGUI.ComponentType.BUTTON,
            callBack = function(self)
                if self.script then
                    local _self = self
                    local env = setmetatable({}, {__index = _G})
                    env.node = self.infoData.chooseNode
                    env.self = self
                    env.this = self
                    if env.node.__cname then
                        env[env.node.__cname] = env.node
                    end
                    env.print = function(...)
                        self:consolePrint(...)
                    end
                    env.clean = function()
                        self:consoleClean()
                    end
                    env.reloadFile = function(file)
                        if not wxStringEndswith(file, ".lua") then
                            file = file .. ".lua"
                        end
                        local fullPath = wxFindPathByFileName(file)
                        if fullPath then
                            fullPath = wxFullpath2Filename(fullPath)
                            self:reloadFile(fullPath)
                        else
                            self:consolePrint("can not find file: " .. file)
                        end
                    end
                    env.removeFile = function(file)
                        if not wxStringEndswith(file, ".lua") then
                            file = file .. ".lua"
                        end
                        local fullPath = wxFindPathByFileName(file)
                        if fullPath then
                            fullPath = wxFullpath2Filename(fullPath)
                            self:reloadFile(fullPath, true)
                        else
                            self:consolePrint("can not find file: " .. file)
                        end
                    end

                    env.dump = function(value, desciption, nesting)
                        if not _DEBUG then
                            return nil
                        end
                        if type(nesting) ~= "number" then
                            nesting = 3
                        end
                        local lookupTable = {}
                        local result = {}
                        local traceback = string.split(debug.traceback("", 2), "\n")
                        self:consolePrint("dump from: " .. string.trim(traceback[3]))
                        local function dump_value_(v)
                            if type(v) == "string" then
                                v = '"' .. v .. '"'
                            end
                            return tostring(v)
                        end
                        local function dump_(value, desciption, indent, nest, keylen)
                            desciption = desciption or "<var>"
                            local spc = ""
                            if type(keylen) == "number" then
                                spc = string.rep(" ", keylen - string.len(dump_value_(desciption)))
                            end
                            if type(value) ~= "table" then
                                result[#result + 1] = string.format("%s%s%s = %s", indent, dump_value_(desciption), spc, dump_value_(value))
                            elseif lookupTable[tostring(value)] then
                                result[#result + 1] = string.format("%s%s%s = *REF*", indent, dump_value_(desciption), spc)
                            else
                                lookupTable[tostring(value)] = true
                                if nest > nesting then
                                    result[#result + 1] = string.format("%s%s = *MAX NESTING*", indent, dump_value_(desciption))
                                else
                                    result[#result + 1] = string.format("%s%s = {", indent, dump_value_(desciption))
                                    local indent2 = indent .. "    "
                                    local keys = {}
                                    local keylen = 0
                                    local values = {}
                                    for k, v in pairs(value) do
                                        keys[#keys + 1] = k
                                        local vk = dump_value_(k)
                                        local vkl = string.len(vk)
                                        if vkl > keylen then
                                            keylen = vkl
                                        end
                                        values[k] = v
                                    end
                                    table.sort(
                                        keys,
                                        function(a, b)
                                            if type(a) == "number" and type(b) == "number" then
                                                return a < b
                                            else
                                                return tostring(a) < tostring(b)
                                            end
                                        end
                                    )
                                    for i, k in ipairs(keys) do
                                        dump_(values[k], k, indent2, nest + 1, keylen)
                                    end
                                    result[#result + 1] = string.format("%s}", indent)
                                end
                            end
                        end
                        dump_(value, desciption, "- ", 1)
                        for i, line in ipairs(result) do
                            self:consolePrint(line)
                        end
                    end
                    env.this.node = env.node
                    local func, errormsg = loadstring(self.script, nil, "t", env)
                    if not func then
                        self:consolePrint("ERROR :" .. errormsg)
                    else
                        xpcall(
                            func,
                            function(msg)
                                self:consolePrint(msg .. "/n" .. debug.traceback())
                            end
                        )
                    end
                end
            end,
            text = UTF8TowxString("执行脚本"),
            force = true,
            ignoreOperatorStack = true,
            lineheight = 50,
            changeLine = true
        },
        {
            type = wxGUI.ComponentType.TEXTBOX,
            title = UTF8TowxString("编译结果:"),
            getValue = function(self)
                return tostring(self:getResult() or "")
            end,
            force = true,
            width = 350,
            height = 100,
            changeLine = true,
            ignoreOperatorStack = true,
            changeLine = true,
            disableEdit = true
        }
    }
}

function wxNodePanel.create(data)
    local panel = wxNodePanel.new(data)
    if panel:init(data) then
        return panel
    end
    return nil
end

function wxNodePanel:init(data)
    self.sx = 0
    self.sy = 0
    self.callBack = data.callBack
    self.infoData = data.infoData
    self.cameras = data.cameras
    self.mapFunctions = {}
    self.mapLastValue = {}
    self.Bindfiles = {}
    self.PathFiles = {}
    self.frame = data.frame
    self.touthEnable = false
    self.enableMoveNode = false
    self.nodePanel = wxGUI:wxScrolledWindow(self.frame, data.width, data.height)
    self.title = wxGUI:wxScrolledWindow(self.nodePanel, data.width, data.height)
    self.panel = wxGUI:wxScrolledWindow(self.nodePanel, data.width, data.height)
    self.layout = wxGUI:createGridLayout(2, 1, 10, 0)
    for i, title in pairs(TITLE) do
        self:createCommponent(title, self.title)
    end
    self.nodePanel:SetSizer(self.layout)
    self.layout:Add(self.title)
    self.layout:Add(self.panel, 0, wxOr(wx.wxEXPAND, wx.wxALL))
    self.layout:AddGrowableRow(1, 1)
    self.layout:AddGrowableCol(0, 0)
    self.wxOperatorStack = wxOperatorStack.create()
    return true
end

function wxNodePanel:checkTouchPanel()
    if not self.touchPanel or tolua.isnull(self.touchPanel) then
        self:createTouchPanel()
        self:initTouchEvent()
        self:initMouseEvent()
    end
    self:updateTouchPanel()
end

function wxNodePanel:updateTouchPanel()
    local drawCamera = self:getCameraByFlag(UI_CAMERA_FLAG)
    if drawCamera and not tolua.isnull(drawCamera) then
        local scalexx, scaleyy = wxCalcCameraZoom(drawCamera)
        self.touchPanel:setContentSize(DESIGN_RESOLUTION_WIDTH * scalexx, DESIGN_RESOLUTION_HEIGHT * scaleyy)
        local position = self.touchPanel:getParent():convertToNodeSpace(cc.p(0, 0))
        self.touchPanel:setPosition(cc.pAdd(position, cc.p(drawCamera:getPosition())))
    end
    self.touchPanel:setLocalZOrder(TOUCH_PANEl_ORDER)
end

function wxNodePanel:getCameraByFlag(flag)
    local Camera = self.cameras[flag]
    if not Camera or tolua.isnull(Camera) then
        self:calculateCamera()
        Camera = self.cameras[flag]
    end
    return Camera
end

function wxNodePanel:createTouchPanel()
    if not self.touchPanel or tolua.isnull(self.touchPanel) then
        local scene = cc.Director:getInstance():getRunningScene()
        self.touchPanel = ccui.Layout:create()
        self.touchPanel.ignore = true
        local layout = ccui.LayoutComponent:bindLayoutComponent(self.touchPanel)
        layout:setSize({width = DESIGN_RESOLUTION_WIDTH, height = DESIGN_RESOLUTION_HEIGHT})
        scene:addChild(self.touchPanel)
    end
end

function wxNodePanel:initTouchEvent()
    local basePosX, basePosY, baseOpacity, rectNodeX, rectNodeY
    local updateMovePos = function(node, x, y, beginX, beginY)
        local camera = self:getCameraByFlag(node:getCameraMask())
        local scaleX, scaleY = 1, 1
        if camera then
            scaleX, scaleY = wxCalcCameraZoom(camera)
        end
        local worldTouchNodePos = node:getParent():convertToWorldSpace(cc.p(beginX, beginY))
        worldTouchNodePos.x = worldTouchNodePos.x + x * scaleX
        worldTouchNodePos.y = worldTouchNodePos.y + y * scaleY
        local nodePositon = node:getParent():convertToNodeSpace(worldTouchNodePos)
        node:setPosition(nodePositon)
        node:setOpacity(DEFAULT_MOVED_OPACITY)
    end
    local function ontouch(sender, event)
        if self.touthEnable or self.moveEnable then
            local targetNode = self:getTouchOrMoveNode()
            local targetNodeRect = self:getTouchOrMoveRect()
            if event == ccui.TouchEventType.began then
                if CheckNodeVaild(targetNode) then
                    self.enableMoveNode = true
                    self.callBack(targetNode)
                    basePosX = targetNode:getPositionX()
                    basePosY = targetNode:getPositionY()
                    rectNodeX = targetNodeRect:getPositionX()
                    rectNodeY = targetNodeRect:getPositionY()
                    baseOpacity = targetNode:getOpacity()
                    return true
                end
            elseif event == ccui.TouchEventType.moved then
                local sPos = sender:getTouchBeganPosition()
                local ePos = sender:getTouchMovePosition()
                local x = ePos.x - sPos.x
                local y = ePos.y - sPos.y
                updateMovePos(targetNode, x, y, basePosX, basePosY)
                updateMovePos(targetNodeRect, x, y, rectNodeX, rectNodeY)
            elseif event == ccui.TouchEventType.ended then
                self.touthEnable = false
                self.enableMoveNode = false
                if targetNode and targetNodeRect then
                    targetNode:setOpacity(baseOpacity)
                    targetNodeRect:setOpacity(255)
                    self.wxOperatorStack:pushOperation(Operation.new(self, targetNode, {set = "setPosition"}, cc.p(basePosX, basePosY), cc.p(targetNode:getPosition())))
                end
                return true
            end
        end
    end
    self.touchPanel:setTouchEnabled(true)
    self.touchPanel:setSwallowTouches(self:getSwallowTouchStatus())
    self.touchPanel:addTouchEventListener(ontouch)
end

function wxNodePanel:refreshMoveNode()
    if self.moveNode and self.moveNode ~= self.chooseNode then
        self:cleanMoveNode()
        if self.moveEnable then
            self.moveNode = self.chooseNode
            self.moveNodeRect = self:drawRect(self.moveNode)
        end
    end
end

function wxNodePanel:cleanMoveNode()
    if self.moveNodeRect and not tolua.isnull(self.moveNodeRect) then
        self.moveNodeRect:removeFromParent()
    end
    self.moveNodeRect = nil
    self.moveNode = nil
end

function wxNodePanel:cleanTouchNode()
    if self.touchNodeRect and not tolua.isnull(self.touchNodeRect) then
        self.touchNodeRect:removeFromParent()
    end
    self.touchNodeRect = nil
    self.touchNode = nil
end

function wxNodePanel:getSwallowTouchStatus()
    return self.touthEnable or self.moveEnable
end

function wxNodePanel:getTouchOrMoveNode()
    if self.touthEnable then
        return self.touchNode
    elseif self.moveEnable then
        return self.moveNode
    end
    return nil
end

function wxNodePanel:getTouchOrMoveRect()
    if self.touthEnable then
        return self.touchNodeRect
    elseif self.moveEnable then
        return self.moveNodeRect
    end
    return nil
end

function wxNodePanel:initMouseEvent()
    local listener = cc.EventListenerMouse:create()
    local eventDispatcher = cc.Director:getInstance():getEventDispatcher()

    local function onMouseMoved(sender)
        local mousePosX = sender:getCursorX()
        local mousePosY = sender:getCursorY()
        self:touchMove(
            {
                x = mousePosX,
                y = mousePosY
            }
        )
    end
    listener:registerScriptHandler(onMouseMoved, cc.Handler.EVENT_MOUSE_MOVE)
    eventDispatcher:addEventListenerWithSceneGraphPriority(listener, self.touchPanel)
end

function wxNodePanel:touchMove(pos)
    self.touchPos = pos
    if not self.moveInterval and self.touthEnable then
        self.moveInterval = true
        wxDelayCall(
            self.touchPanel,
            function()
                if not self.enableMoveNode then
                    self:calculateChooseNode()
                end
                self.moveInterval = false
            end,
            0.1
        )
    end
end

function wxNodePanel:calculateChooseNode()
    self.preTouchNode = self:calculateTouchNode()
end

function wxNodePanel:CheckCameraEnable(cameraMask)
    local camera = self:getCameraByFlag(cameraMask)
    if camera and (self.OpenWorldCamera or cameraMask == UI_CAMERA_FLAG) then
        return true
    end
    return false
end

function wxNodePanel:CheckIsTouch(node)
    local mask = node:getCameraMask()
    if self:CheckCameraEnable(mask) then
        if wxCheckDrawEnable(node) then
            if HitTest(node, self.touchPosInDifferentCamera[mask]) then
                return true
            end
        end
    end
    return false
end

function wxNodePanel:CalculateTouchPosInCameras()
    self.touchPosInDifferentCamera = {}
    if self.cameras and next(self.cameras) then
        for mask, camera in pairs(self.cameras) do
            self.touchPosInDifferentCamera[mask] = wxTouchPanel2World(self.touchPos, camera, self:getCameraByFlag(UI_CAMERA_FLAG))
        end
    end
end

function wxNodePanel:calculateTouchNode()
    self:CalculateTouchPosInCameras()
    local scene = cc.Director:getInstance():getRunningScene()
    local searchNode
    searchNode = function(parent)
        local touchNode = nil
        local children = parent:getChildren()
        if children and next(children) then
            local childrenCount = #children
            for i = 0, childrenCount - 1 do
                local child = children[childrenCount - i]
                if child:isVisible() and not child.ignore then
                    if self:CheckIsTouch(child) then
                        touchNode = child
                    end
                    local touchNode2 = searchNode(child)
                    if touchNode2 then
                        touchNode = touchNode2
                    end
                    if touchNode then
                        return touchNode
                    end
                end
            end
        end
    end
    return searchNode(scene)
end

function wxNodePanel:wxCalculateToworldPos(node, pos)
    local camera = self:getCameraByFlag(node:getCameraMask())
    if camera then
        return wxTouchPanel2World(pos, camera, self:getCameraByFlag(UI_CAMERA_FLAG))
    end
    return pos
end

function wxNodePanel:wxCalculateTopanelPos(node, pos)
    local camera = self:getCameraByFlag(node:getCameraMask())
    if camera then
        return wxWorld2TouchPanel(pos, camera, self:getCameraByFlag(UI_CAMERA_FLAG))
    end
    return pos
end

function wxNodePanel:AddCamera(camera)
    self.cameras = self.cameras or {}
    if camera and not tolua.isnull(camera) then
        self.cameras[camera:getCameraFlag()] = camera
    end
end

function wxNodePanel:ctor(data)
end

function wxNodePanel:refreshNode()
    self:checkTouchPanel()
    self:checkDeleteItem()
    self:updateChooseNode()
    self:refreshDrawRect()
end

function wxNodePanel:updateChooseNode()
    if self.chooseNode ~= self.infoData.chooseNode then
        self:deleteAllItem()
        self.chooseNode = self.infoData.chooseNode
        self:consoleClean()
    --self:refreshBindFile()
    end
    if self.chooseNode and not tolua.isnull(self.chooseNode) then
        self:refreshAllItems()
    end
    self:refreshAllCommponent()
end

function wxNodePanel:checkDeleteItem()
    if tolua.isnull(self.infoData.chooseNode) then
        self.chooseNode = nil
        self:deleteAllItem()
    end
end

function wxNodePanel:refreshAllItems()
    self:refreshMoveNode()
    self:refreshTitle()
    self:refreshAllFunction()
end

function wxNodePanel:refreshDrawRect()
    if self.preTouchNode and self.touthEnable and not self.moveEnable then
        if self.preTouchNode ~= self.touchNode then
            self.touchNode = self.preTouchNode
            if self.touchNodeRect then
                self.touchNodeRect:removeFromParent()
                self.touchNodeRect = nil
            end
            if self.touchNode then
                self.touchNodeRect = self:drawRect(self.touchNode)
            end
        end
    else
        if self.touchNodeRect then
            self.touchNodeRect:removeFromParent()
            self.touchNodeRect = nil
            self.touchNode = nil
        end
    end
end

function wxNodePanel:refreshTitle()
    local nodeName = GetNodeName(self.chooseNode)
    if self.frameName ~= nodeName then
        self.frameName = nodeName
        self.frame:SetTitle(UTF8TowxString(self.frameName))
    end
end

function wxNodePanel:refreshBindFile()
    if self.infoData.chooseNode and not tolua.isnull(self.infoData.chooseNode) then
        if not self.nodeBindfile then
            self.nodeBindfile = self:calcBindFile()
        end
    end
end

function wxNodePanel:calcBindFile()
    self.Bindfiles = self.Bindfiles or {}
    local functionInfo = wxGetNodeUsefullFunction(self.infoData.chooseNode)
    local bindFile = self.Bindfiles[functionInfo] or wxFindFunctionSource(functionInfo)
    self.Bindfiles[functionInfo] = bindFile or ""
    return self.Bindfiles[functionInfo]
end

function wxNodePanel:calcPathFile()
    self.PathFiles = self.PathFiles or {}
    local functionInfo = wxGetNodeUsefullFunction(self.infoData.chooseNode)
    local pathFile = self.PathFiles[functionInfo]
    if not pathFile then
        local bindFile = self.Bindfiles[functionInfo] or self:calcBindFile()
        pathFile = wxFullpath2Filename(bindFile)
    end
    self.PathFiles[functionInfo] = pathFile or ""
    return self.PathFiles[functionInfo]
end

function wxNodePanel:refreshAllFunction()
    self.sx = 0
    self.sy = 0
    local deepTree = nil
    deepTree = function(children)
        for name, child in pairs(children) do
            self:createCommponent(child, self.panel)
            if child.children then
                deepTree(child.children)
            end
        end
    end
    if not self.mapFunctions[self.panel] or not next(self.mapFunctions[self.panel]) then
        deepTree(layoutTable.children)
    end
end

function wxNodePanel:refreshAllCommponent()
    for panel, datas in pairs(self.mapFunctions) do
        if datas and next(datas) then
            for comp, data in pairs(datas) do
                self:refreshCommponent(comp, data, panel)
            end
        end
    end
end

function wxNodePanel:refreshCommponent(component, data, panel)
    if not self.mapLastValue[panel] then
        self.mapLastValue[panel] = {}
        return
    end
    if self.mapLastValue[panel][data] == nil then
        return
    end
    local getValue = function()
        if data.get and self.chooseNode and not tolua.isnull(self.chooseNode) then
            return self.chooseNode[data.get] and self.chooseNode[data.get](self.chooseNode) or nil
        end
        if data.getValue then
            return data.getValue(self, self.chooseNode)
        end
        return nil
    end
    local value = getValue()
    if data.transDataFromCocos then
        value = data.transDataFromCocos(value, self.chooseNode)
    end
    if self.mapLastValue[panel][data] == value then
        return
    end
    if data.type == wxGUI.ComponentType.BUTTON then
    elseif data.type == wxGUI.ComponentType.EDITBOX then
        component:ChangeValue(UTF8TowxString(value))
    elseif data.type == wxGUI.ComponentType.TEXTBOX then
        component:ChangeValue(UTF8TowxString(value))
    elseif data.type == wxGUI.ComponentType.COLORPICK then
        component:SetBackgroundColour(value)
    elseif data.type == wxGUI.ComponentType.CHECKBOX then
        component:SetValue(value)
    end
    self.mapLastValue[panel][data] = value
end

function wxNodePanel:createCommponent(data, panel)
    self.mapFunctions[panel] = self.mapFunctions[panel] or {}
    self.mapLastValue[panel] = self.mapLastValue[panel] or {}
    local chooseNode = self.chooseNode
    if data.force or chooseNode[data.set] or (data.visible and data.visible(self)) then
        if data.changeLine then
            self.sx = 0
            self.sy = self.sy + ((self.height and self.height ~= 0) and (self.height + 5) or DEFAULT_FUNCTION_HEIGHT)
        end
        if data.space then
            self.sx = self.sx + data.space.x
        end
        if data.lineheight then
            self.height = data.lineheight
        else
            self.height = nil
        end
        if data.title and data.title ~= "" then
            local text = wxGUI:wxCreateLabel(panel, data.title)
            text:Move(wx.wxPoint(self.sx, self.sy))
            if not data.nospace then
                self.sx = self.sx + DEFAULT_TEXT_WIDTH
            else
                self.sx = self.sx + DEFAULT_TEXT_WIDTH - 100
            end
        end
        local getValue = function(chooseNode)
            if data.get and chooseNode and not tolua.isnull(chooseNode) then
                return chooseNode[data.get] and chooseNode[data.get](chooseNode) or nil
            end
            if data.getValue then
                return data.getValue(self, chooseNode)
            end
            return nil
        end
        local component = nil
        local callback =
            handler(
            self,
            function(this, value)
                local oldValue = getValue(this.chooseNode)
                if data.set then
                    local chooseNode = this.chooseNode
                    if chooseNode and not tolua.isnull(chooseNode) then
                        if data.transDataFromWx then
                            value = data.transDataFromWx(value, chooseNode)
                        end
                        if self.mapLastValue[panel][data] == value then
                            return
                        end
                        if value or value == false then
                            chooseNode[data.set](chooseNode, value)
                            self.mapLastValue[panel][data] = value
                        end
                        if not data.ignoreOperatorStack then
                            self.wxOperatorStack:pushOperation(Operation.new(self, chooseNode, data, oldValue, value))
                        end
                    end
                else
                    if data.callBack then
                        if data.transDataFromWx then
                            value = data.transDataFromWx(value)
                        end
                        if value ~= nil then
                            if self.mapLastValue[panel][data] ~= value then
                                self.mapLastValue[panel][data] = value
                                data.callBack(this, value)
                                if not data.ignoreOperatorStack then
                                    self.wxOperatorStack:pushOperation(Operation.new(self, chooseNode, data, oldValue, value))
                                end
                            end
                        end
                    end
                end
            end
        )
        local value = getValue(self.chooseNode)
        if data.transDataFromCocos then
            value = data.transDataFromCocos(value, self.chooseNode)
        end
        if data.type == wxGUI.ComponentType.BUTTON then
            component = wxGUI:wxCreateButton(panel, data.text, data.width, data.height, callback)
        elseif data.type == wxGUI.ComponentType.EDITBOX then
            component = wxGUI:wxCreateEditBox(panel, data.width, data.height, not data.disableEdit, callback)
            component:ChangeValue(UTF8TowxString(value))
        elseif data.type == wxGUI.ComponentType.TEXTBOX then
            component = wxGUI:wxCreateTextBox(panel, data.width, data.height, not data.disableEdit, callback)
            component:ChangeValue(UTF8TowxString(value))
        elseif data.type == wxGUI.ComponentType.COLORPICK then
            component = wxGUI:wxCreateColorPick(panel, data.width, data.height, value, callback)
        elseif data.type == wxGUI.ComponentType.CHECKBOX then
            component = wxGUI:wxCreateCheckBox(panel, data.text, data.width, data.height, callback)
            component:SetValue(value and true or false)
        end
        if component then
            component:Move(wx.wxPoint(self.sx, self.sy))
            self.mapFunctions[panel][component] = data
            self.mapLastValue[panel][data] = value
        end
    end
end

function wxNodePanel:deleteAllItem()
    self.panel:DestroyChildren()
    self.mapFunctions[self.panel] = {}
    self.mapLastValue[self.panel] = {}
    self.nodeBindfile = nil
    if self.defaultSizer then
        self.defaultSizer:Clear()
    end
end

function wxNodePanel:consoleClean()
    self.result = ""
end

function wxNodePanel:getResult()
    return self.result
end

function wxNodePanel:consolePrint(...)
    self.result = self.result or ""
    local args = {...}
    local str = ""
    for i, value in ipairs(args) do
        str = str .. tostring(value)
        print(value)
    end
    self.result = self.result .. str
    self.result = self.result .. "\n"
end

function wxNodePanel:openFile(bindfile)
    if bindfile and bindfile ~= "" then
        local code = "code " .. bindfile
        self:consolePrint("vscode open file：", bindfile)
        os.execute(code)
    else
        self:consolePrint("node file is nil")
    end
end

function wxNodePanel:reloadFile(nodeFile, unload)
    local reload = function(file)
        if package.loaded[file] then
            self:consolePrint("unload file：", file)
            package.loaded[file] = nil
            if not unload then
                self:consolePrint("load file：", file)
                require(file)
            end
            return true
        end
        return false
    end
    if nodeFile and nodeFile ~= "" then
        if not reload(nodeFile) then
            local noluaFile = string.gsub(nodeFile, "%.lua$", "")
            reload(noluaFile)
        end
    else
        self:consolePrint("node file is nil")
    end
end

function wxNodePanel:calculateCamera()
    self.cameras = {}
    local scene = cc.Director:getInstance():getRunningScene()
    local carmers = wxFindAllChildrenWithType(scene, "cc.Camera")
    for i, camera in pairs(carmers) do
        self:AddCamera(camera)
    end
end

function wxNodePanel:drawSoloRect(drawNode, width, height, anchorPoint)
    drawNode:drawSolidRect(cc.p(-anchorPoint.x * width, -anchorPoint.y * height), cc.p(width * (1 - anchorPoint.x), height * (1 - anchorPoint.y)), cc.c4f(1, 0, 0, 0.5))
    drawNode:drawPoint({x = 0, y = 0}, 5, cc.c4f(0, 0, 1, 0.5))
end

function wxNodePanel:drawSolidCircle(drawNode, r)
    drawNode:setAnchorPoint(cc.p(0.5, 0.5))
    drawNode:drawSolidCircle(cc.p(0, 0), r, 0, 30, 1, 1, cc.c4f(0, 1, 0, 0.5))
    drawNode:drawPoint(cc.p(0, 0), 5, cc.c4f(0, 0, 1, 0.5))
end

function wxNodePanel:drawRect(rectNode, time)
    local drawCamera = self:getCameraByFlag(UI_CAMERA_FLAG)
    if not drawCamera or tolua.isnull(drawCamera) then
        return
    end
    local scalexx, scaleyy = wxCalcCameraZoom(drawCamera)
    local drawNode = cc.DrawNode:create()
    drawNode.ignore = true
    self.touchPanel:addChild(drawNode)
    if CheckNodeVaild(rectNode) then
        local camera = self:getCameraByFlag(rectNode:getCameraMask())
        local scaleX, scaleY = wxCalcCameraZoom(camera)
        local worldpos = rectNode:getParent():convertToWorldSpace(cc.p(rectNode:getPosition()))
        local panelworldpos = self:wxCalculateTopanelPos(rectNode, worldpos)
        drawNode:setPosition(drawNode:getParent():convertToNodeSpace(panelworldpos))
        local contentSize = wxGetNodeWorldContentSize(rectNode)
        local width = math.abs(contentSize.width / scaleX * scalexx)
        local height = math.abs(contentSize.height / scaleY * scaleyy)
        local anchorPoint = rectNode:getAnchorPoint()
        drawNode:setAnchorPoint(anchorPoint)
        if width > 0 and height > 0 then
            self:drawSoloRect(drawNode, width, height, anchorPoint)
        else
            self:drawSolidCircle(drawNode, CIRCLE_R * math.max(scalexx, scaleyy))
        end
    else
        local size = self.touchPanel:getContentSize()
        drawNode:setPosition(cc.p(size.width / 2, size.height / 2))
        self:drawSolidCircle(drawNode, CIRCLE_R * math.max(scalexx, scaleyy))
    end
    if time then
        wxDelayCall(
            drawNode,
            function()
                drawNode:removeFromParent()
            end,
            time
        )
    end
    return drawNode
end

return wxNodePanel
